package demo.topic12.proxy;

import org.junit.Test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理模式（反射实现）
 * 0. 动态代理模式在框架底层实现中被大量使用到，著名的Spring框架中的AOP的底层就是动态代理。
 * 1. 概念：所谓动态代理，即通过代理类：Proxy的代理，接口和实现类之间可以不直接发生联系，而可以在运行期（Runtime）实现动态关联。
 * 2. java动态代理主要是使用 java.lang.reflect 包中的两个类
 * （1）InvocationHandler类
 * （1.1）public Object invoke(Object obj, Method method, Object[] obs)
 * （1.2）其中第一个参数obj指的是代理类，method是被代理的方法，obs是指被代理的方法的参数组。此方法由代理类来实现。
 *
 * Proxy类
 * 1. protected Proxy(InvocationHandler h)
 * 2. static Class getProxyClass(ClassLoader loader, Class[] interfaces)
 * 3. static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
 * 4. 动态代理其实是运行时生成class，所以我们必须提供一组interface，然后告诉它class已经实现了这些interface，
 * 而且在生成Proxy的时候，必须给他提供一个handler，让它来接管实际的工作。
 *
 * 类加载器的原理分析（JVM底层原理）
 * 1. 类加载的过程
 * （1）JVM将类加载过程分为三个步骤：装载（Load）、链接（Link）和初始化（Initialize），链接又分为三个步骤。
 * （1.0）装载->链接（验证->准备->解析）->初始化
 * （1.1）装载：查找并加载类的二进制数据。
 * （1.2）链接：
 * （1.2.1）验证：确保被加载类的正确性
 * （1.2.2）准备：为类的静态变量分配内存，并将其初始化为默认值
 * （1.2.3）解析：把类中的符号引用转换为直接引用
 * （1.3）初始化：为类的静态变量赋予正确的初始值
 * 2. 类的初始化，类什么时候初始化
 * （1）创建类的实例，也就是new一个对象
 * （2）访问某个类或接口的静态变量，或者对该静态变量赋值
 * （3）调用类的静态方法
 * （4）反射（Class.forName("com.vince.Dog")）
 * （5）初始化一个类的子类（会首先初始化子类的父类）
 * （6）JVM启动时标明的启动类，即文件名和类名相同的那个类
 * 3. 类的加载
 * （1）指的是将类的.class文件中的二进制数据读入到内存中，将其放在运行时数据区的方法区内，然后在堆区创建一个这个类
 * 的Java.lang.Class对象，用来封装类在方法区类的对象。
 */
public class ProxyDemo2 {
    private interface Subject {
        public void shopping();
    }

    private interface Hotel {
        public void reserve();
    }

    private static class Person implements Subject, Hotel {
        @Override
        public void shopping() {
            System.out.println("客户：付款，买到心仪的物品");
        }

        @Override
        public void reserve() {
            System.out.println("客户：付预订款，打飞的前往目的地");
        }
    }

    @Test
    public void testProxy() {
        CreateProxy cp = new CreateProxy();
        Subject person = new Person();
        Subject proxy = (Subject) cp.create(person);
        proxy.shopping();
    }

    @Test
    public void testProxy2() {
        CreateProxy cp = new CreateProxy();
        Hotel person = new Person();
        Hotel proxy = (Hotel) cp.create(person);
        proxy.reserve();
    }
}

/**
 * 用于动态生成一个代理对象
 */
class CreateProxy implements InvocationHandler {

    private Object target; // 被代理对象

    // 用于创建代理对象的方法
    public Object create(Object target) {
        this.target = target;
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
        return proxy;
    }

    /**
     * 代理对象要执行的方法
     * @param proxy 代理类对象
     * @param method 被代理对象的方法
     * @param args 被代理对象方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理：上海外寻找客户需要的产品...");
        System.out.println("代理：跟客户确认物品");
        method.invoke(target, args); // 执行代理方法
        System.out.println("代理：完成本次代购");
        return null;
    }
}
